---
title: Interactive shader
---

In this tutorial, we'll walk you through the process of configuring a shader-based material while leveraging Threlte's built-in interactivity plugin. Specifically, you'll learn how to dynamically adjust material uniforms based on user interactions—such as clicking on the mesh.

<Example path="shaders/interactiveShader" />

## How does it work?

We'll start this example by utilizing the mesh terrain established in one of our other examples, [Terrain with 3D noise](/docs/examples/geometry/terrain-with-3d-noise), as our foundational starting point.

### The shader material

To integrate the shader-based material into your terrain mesh, simply nest the `<T.ShaderMaterial/>` component as a child element. It's essential to supply both `fragmentShader` and `vertexShader` props, formatted as strings.

In our example, these shaders are isolated into individual files — `fragment.glsl` for the fragment shader and `vertex.glsl` for the vertex shader. These files are imported using Vite's `?raw` special query which imports them as raw strings. Although your build tool and setup might differ, this modular approach enhances code readability. Alternatively, you could provide these shaders directly as JavaScript strings.

```svelte title="Scene.svelte" {2-3}+ {10-13}+
<script>
  import fragmentShader from './fragment.glsl?raw'
  import vertexShader from './vertex.glsl?raw'
</script>

<T.Mesh
  {geometry}
  rotation.x={MathUtils.DEG2RAD * -90}

  <T.ShaderMaterial
    {fragmentShader}
    {vertexShader}
  />
</T.Mesh>
```

### Terrain Interactivity

In this step, we set up interactivity for the Terrain Mesh. The aim is to trigger a shader animation when the user clicks on the mesh, thereby updating its variables in real-time.

To accomplish this, we first import and initialize the [interactivity](/docs/reference/extras/interactivity) plugin from `@threlte/extras`. This extends the functionality of meshes by enabling `onclick` events, akin to the familiar HTML events in Svelte. For our animation, it's crucial to pinpoint the exact location where the user clicks on the mesh.
The event generated by the plugin conveniently provides us with a `point` variable to identify this location.

```svelte title="Scene.svelte" {2,3}+ {9-11}+
<script>
  import { interactivity } from '@threlte/extras'
  interactivity()
</script>

<T.Mesh
  {geometry}
  rotation.x={MathUtils.DEG2RAD * -90}
  onclick={({ point }) => {
    console.log('user clicked on', point)
  }}
>
  <T.ShaderMaterial
    {fragmentShader}
    {vertexShader}
  />
</T.Mesh>
```

### Shader Interactivity

With event listeners now active on our mesh, the next step is to capture these events into variables and forward them to the shader as uniforms.
Our shader will require two uniform variables: one for the click position (`pulsePosition` as a `Vector3`) and another for tracking the
animation timeline (`pulseTimer`).

To manage the timeline, we'll employ a Svelte store using the [Tween](https://svelte.dev/docs/svelte-motion#Tween) class.
Both `pulsePosition` and `pulseTimer` will be updated in the `onclick` event callback.

Configuring uniforms for our `ShaderMaterial` is straightforward and closely aligns with standard Three.js practices.
To update the `pulseTimer` and `pulsePosition` uniform value, we'll use a pierced property: `uniforms.pulseTimer.value={pulseTimer.current}`.

```svelte title="Scene.svelte" {2-8}+ {15-22}+ {28-37}+
<script>
  import { Tween } from 'svelte/motion'
  import { quadOut } from 'svelte/easing'

  const pulsePosition = new Vector3()
  const pulseTimer = new Tween(0, {
    easing: quadOut
  })
</script>

<T.Mesh
  {geometry}
  rotation.x={DEG2RAD * -90}
  onclick={({ point }) => {
    pulsePosition.copy(point)
    pulseTimer
      .set(0, {
        duration: 0
      })
      .then(() => {
        pulseTimer.set(1, { duration: 2000 })
      })
  }}
>
  <T.ShaderMaterial
    {fragmentShader}
    {vertexShader}
    uniforms={{
      pulseTimer: {
        value: 0
      },
      pulsePosition: {
        value: 0
      }
    }}
    uniforms.pulsePosition.value={pulsePosition}
    uniforms.pulseTimer.value={pulseTimer}
  />
</T.Mesh>
```

### How do the fragment and vertex shaders work?

While a deep dive into shader mechanics falls beyond the scope of this tutorial, let's take a moment for a brief conceptual overview:

1. **Vertex shader**: At its core, we have a straightforward vertex shader that forwards the world position of the material to the fragment shader. This is achieved using a `varying vPosition` variable, along with UV coordinates transferred through a `varying vUv` variable.
2. **Fragment shader and user interaction**: Armed with knowledge of the material's world position, we can dynamically render a circle originating from the `pulsePosition` uniform, which is set by the user's click event. As time progresses, the circle's radius expands, controlled by the `pulseTimer` uniform.

If you want to learn more about how to write shaders, the [Book of Shaders](https://thebookofshaders.com/) is an excellent resource to start with.
